﻿#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import platform
import os
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
from PySide.QtNetwork import *

_FRAME_WIDTH = 332
_FRAME_HEIGHT = 500
_HEADER_HEIGHT = 43
_FOOTER_HEIGHT = 49
_FOOTER_BUTTON_WIDTH = _FRAME_WIDTH / 3

# 객체 반환 함수 
# 이름에 해당하는 객체를 반환합니다.
#
def object(name):
	for widget in QApplication.allWidgets():
		if widget.objectName() == name:
			return widget
	
	return ''

# 웹뷰 클래스
# UI(HTML/Javascript/CSS)를 표현하고 네이티브 코드와 연동하는 역할을 합니다.
#
class WebView(QWebView):
	def __init__(self, parent = None):
		super(WebView, self).__init__(parent)				
		self.nativeAdapter = NativeAdapter(self);
		
		# 핫키 매니저 생성
		self.hotkeyManager = HotkeyManager(self)
		
		# 시그널 & 슬롯 등록
		self.loadStarted.connect(self.onLoadStarted)
		self.loadFinished.connect(self.onLoadFinished)
		
		# 자바스크립트 활성화
		self.settings().setAttribute(QWebSettings.JavascriptEnabled, True)
		
		# 수평 스크롤바 비활성화 
		self.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
		#self.page().mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
		
	@Slot()
	def onLoadStarted(self):
		# 네이티브 객체를 자바스크립트 객체로 바인딩
		self.page().mainFrame().addToJavaScriptWindowObject("nativeAdapter", self.nativeAdapter)
	
	@Slot(bool)	
	def onLoadFinished(self, ok):
		object('MainWindow').ready()
		
	def sendMessage(self, message, arg1 = '', arg2 = '', arg3 = ''):
		self.page().mainFrame().evaluateJavaScript('handleMessage("%s", "%s", "%s", "%s")' % (message, arg1, arg2, arg3))
		
	def dropEvent(self, event):
		programUrls = event.mimeData().urls()
		self.hotkeyManager.add(programUrls)
			#codec = QTextCodec.codecForLocale()
			#codec.toUnicode(program)
			#print program

	def hotkeyMgr(self):
		return self.hotkeyManager
	
# 웹과 네이티브를 연결하는 클래스
# 웹과 네이티브 API를 연결하는 역할을 합니다.
#
class NativeAdapter(QObject):
	def __init__(self, parent = None):
		super(NativeAdapter, self).__init__(parent)
	
	@Slot(str, str, str)
	def handleMessage(self, message, arg1, arg2):
		print '[NativeAdapter.handleMessage]', message, arg1, arg2
			
		if message == 'execute': # arg1은 실행할 인덱스를 가리킵니다.
			hotkeyManager = object('MainWindow').webView().hotkeyMgr()
			path = hotkeyManager.path(arg1)
			if hotkeyManager.isExecutable(arg1):
				print '[QProcess.startDetached] start'
				ret = QProcess.startDetached("\"" + path + "\"")
				# 리눅스는 실행 권한만 있으면 Executable로 인식됨. 따라서 QProcess가 실패하면 QDesktopServices로 다시 처리.
				if platform.system() == 'Linux' and ret == False:
					ret = QDesktopServices.openUrl(QUrl('file:///' + path))
			else:
				print '[QDesktopServices.openUrl] start'
				ret = QDesktopServices.openUrl(QUrl('file:///' + path))
			print 'exec', ret, path
			
		elif message == 'remove': # arg1은 실행할 인덱스의 리스트입니다.
			for index in reversed(arg1):
				if index != ',':
					object('MainWindow').webView().hotkeyMgr().remove(index)
					
		elif message == 'hotkey': # arg1은 인덱스, arg2은 핫키 문자열입니다. (ex. 'Ctrl + F1')
			key = ''
			controlKeys = []
			
			arg2 = arg2.replace(' ', '')
			hotkeys = arg2.split('+')
			
			for hotkey in hotkeys:
				if hotkey == 'Ctrl' or hotkey == 'Alt' or hotkey == 'Shift':
					controlKeys.append(hotkey)
				else:
					key = hotkey
			
			hotkeyManager = object('MainWindow').webView().hotkeyMgr()
			hotkeyManager.setKey(arg1, key)
			hotkeyManager.setControlKeys(arg1, controlKeys)
			
			print key, controlKeys
			
			# 로컬에 정보 저장 
			hotkeyManager.saveData()
			
		elif message == 'restore':
			hotkeyManager = object('MainWindow').webView().hotkeyMgr()		
			hotkeyManager.restoreData()
			
			# 설정 정보 UI로 전달
			settings = Settings()
			settings.beginGroup('MainWindow')
			autorun = settings.value('Autorun', 'on')
			topmost = settings.value('Topmost', 'off')
			settings.endGroup()
			object('MainWindow').webView().sendMessage('setup', autorun, topmost);
			
		elif message == 'setup':
			mainWindow = object('MainWindow')
			
			if arg1 == 'autorun':
				if arg2 == 'on':
					mainWindow.enableAutorun(True);
				else:
					mainWindow.enableAutorun(False);

				mainWindow.writeSettingsAutorun(arg2)
			elif arg1 == 'topmost':
				if arg2 == 'on':
					mainWindow.enableTopmost(True);
				else:
					mainWindow.enableTopmost(False);
				
				mainWindow.writeSettingsTopmost(arg2)
				
		elif message == 'openUrl':
			url = arg1.lower()
			if url.find("http://") == -1:
				url = 'http://' + url
				
			QDesktopServices.openUrl(QUrl(url))
		
# 핫키 데이터를 관리하는 클래스
# 핫키 추가, 삭제, 아이콘 추출 관리를합니다.
#
class HotkeyManager(QObject):
	def __init__(self, parent = None):
		super(HotkeyManager, self).__init__(parent)
		self.hotkeys = []
	
	def add(self, programUrls):
		for programUrl in programUrls:
			path = programUrl.path()
			
			# 윈도우즈는 절대 경로 표시를 제거
			if platform.system() == 'Windows':
				if path[0] == '/' or path[0] == '\\':
					path = path[1:]
			
			fileInfo = QFileInfo(path)			
			name = fileInfo.baseName()
			
			# 아이콘 파일 저장
			iconFile = ''
			if fileInfo.isSymLink():
				iconFile = self.icon(fileInfo.symLinkTarget())
			else:
				iconFile = self.icon(path)

			# 파일 경로 저장 - 파일 속성이 실행이 아닌 바로가기면 해당 경로를 그대로 사용 (for Windows)
			if fileInfo.isSymLink() and fileInfo.isExecutable():
				path = fileInfo.symLinkTarget()
			
			# 실행 파일인지 혹은 윈도우즈의 바로가기 파일인지 판단 
			isExecutable = False
			isShortcurt = False
			if fileInfo.isFile() and fileInfo.isExecutable():
				isExecutable = True
			elif fileInfo.suffix() == 'url' or fileInfo.suffix() == 'lnk':
				isShortcurt = True		
			
			self.hotkeys.append(HotkeyData(name, path, isExecutable, iconFile))							
			print '[add] legnth of hotkey:', len(self.hotkeys), '[EXE]', isExecutable, '[SHORTCURT]', isShortcurt, iconFile
			
			# UI로 메시지 전달
			object('MainWindow').webView().sendMessage('add', iconFile, name)
			
			# 로컬에 정보 저장 
			self.saveData()
	
	def icon(self, path):
		iconProvider = QFileIconProvider()
		fileInfo = QFileInfo(path)
		icon = iconProvider.icon(fileInfo)
		
		# 흑백으로 아이콘 저장
		pixmap = icon.pixmap(48, 48, QIcon.Disabled)
		iconFile = fileInfo.fileName() + '.png.gray'
		pixmap.save(QDir.currentPath() + "/html2/icons/" + iconFile, "PNG")
		
		# 아이콘 저장
		pixmap = icon.pixmap(48, 48)
		iconFile = fileInfo.fileName() + '.png'
		pixmap.save(QDir.currentPath() + "/html2/icons/" + iconFile, "PNG")
		
		return 'icons/' + iconFile
	
	def remove(self, index):
		# 아이콘 파일 삭제 
		print 'html2/' + self.hotkeys[int(index)].icon()
		QFile.remove('html2/' + self.hotkeys[int(index)].icon())
		QFile.remove('html2/' + self.hotkeys[int(index)].icon() + '.gray')
		
		# 리스트에서 제거
		del self.hotkeys[int(index)]	
		print '[remove] legnth of hotkey:', len(self.hotkeys)		
		
		# 로컬에 정보 저장 
		self.saveData()		
	
	def name(self, index):
		return self.hotkeys[int(index)].name()
	
	def path(self, index):
		return self.hotkeys[int(index)].path()
		
	def isExecutable(self, index):
		return self.hotkeys[int(index)].isExecutable()
		
	def setKey(self, index, key):
		self.hotkeys[int(index)].setKey(key)

	def setControlKeys(self, index, keys):
		self.hotkeys[int(index)].setControlKeys(keys)
	
	def saveData(self):
		#  아이템 리스트 저장
		settings = Settings()
		settings.removeGroup('Items')
		settings.beginWriteArray('Items')
		for index in range(len(self.hotkeys)):
			settings.setArrayIndex(index)
			settings.setValue('Name', self.hotkeys[index].name())
			settings.setValue('Path', self.hotkeys[index].path())
			settings.setValue('Icon', self.hotkeys[index].icon())
			settings.setValue('Executable', self.hotkeys[index].isExecutable())
			settings.setValue('Key', self.hotkeys[index].key())
			settings.setValue('ControlKeys', self.hotkeys[index].controlKeys())
		settings.endArray()
	
	def restoreData(self):
		settings = Settings()
		size = settings.beginReadArray('Items')
		for index in range(size):
			settings.setArrayIndex(index)
			name = settings.value("Name")
			path = settings.value("Path")
			icon = settings.value("Icon")			
			key = settings.value("Key")
			controlKeys = settings.value("ControlKeys")
			if settings.value("Executable") == 'true':
				executable = True
			else:
				executable = False
			print name, path, icon, executable, key, controlKeys
			
			# 데이터 객체 생성
			item = HotkeyData(name, path, executable, icon)
			item.setKey(key)
			item.setControlKeys(controlKeys)
			self.hotkeys.append(item)
			
			# 핫키 정보 조합
			hotkey = ''			
			if controlKeys != None:
				if isinstance(controlKeys, list): 
					for controlKey in controlKeys:
						hotkey += controlKey + ' + '				
				else: # 아이템이 하나만 있는 상태
					hotkey = controlKeys + ' + '
				
			if len(key):
				hotkey += key
			else:
				hotkey = hotkey[0:-3]
			
			# UI로 메시지 전달
			object('MainWindow').webView().sendMessage('restore', icon, name, hotkey)					
		settings.endArray()
		
# 핫키 관리 자료형
# 핫키의 정보를 담고 있는 클래스입니다.
#
class HotkeyData(QObject):
	def __init__(self, programName, programPath, isExecutable, icon, parent = None):
		super(HotkeyData, self).__init__(parent)
		self.programName = programName
		self.programPath = programPath
		self.executable = isExecutable
		self._icon = icon
		self._key = ''
		self._controlKeys = ''

	def name(self):
		return self.programName
		
	def path(self):
		return self.programPath
	
	def isExecutable(self):
		return self.executable

	def icon(self):
		return self._icon
	
	def key(self):
		return self._key
	
	def setKey(self, key):
		self._key = key
	
	def controlKeys(self):
		return self._controlKeys
	
	def setControlKeys(self, keys):
		self._controlKeys = keys

# 핫키 및 설정 정보 관리 클래스
# 핫키 및  설정에 대한 정보를 로컬에 저장하고, 읽습니다.
#
class Settings(QObject):
	def __init__(self, parent = None):
		super(Settings, self).__init__(parent)
		self.settings = QSettings(QDir.toNativeSeparators(QDir.currentPath() + '/monkeyhotkey.ini'), QSettings.IniFormat)
	
	def beginGroup(self, group):
		self.settings.beginGroup(group)

	def endGroup(self):
		self.settings.endGroup()
		
#	def valueGroup(self, group, key, defaultValue = None):
#		self.settings.beginGroup(group)
#		value = self.settings.value(key, defaultValue)	
#		self.settings.endGroup()
#		return value

	def value(self, key, defaultValue = None):
		return self.settings.value(key, defaultValue)
			
#	def setValueGroup(self, group, key, value):
#		self.settings.beginGroup(group)
#		self.settings.setValue(key, value)	
#		self.settings.endGroup()

	def setValue(self, key, value):
		self.settings.setValue(key, value)	
				
	def removeGroup(self, group):
		self.settings.beginGroup(group)
		self.settings.remove("")
		self.settings.endGroup()

	def beginReadArray(self, group):
		return self.settings.beginReadArray(group)
				
	def beginWriteArray(self, group):
		self.settings.beginWriteArray(group)
	
	def endArray(self):
		self.settings.endArray()

	def setArrayIndex(self, i):
		self.settings.setArrayIndex(i)
		
	def contains(self, key):
		return self.settings.contains(key)
		
# 둥근 버튼 클래스
# 제목 표시줄에 내장되는 버튼입니다.
#
class CircleButton(QWidget):
	clicked = Signal()
	
	def __init__(self, fileName, backgroundColor, parent = None):
		super(CircleButton, self).__init__(parent)
		self.resize(26, 26)
		
		# 서클 버튼 테두리
		self.container = QPushButton(self)
		self.container.setFocusPolicy(Qt.NoFocus)
		self.container.clicked.connect(self.clicked)
		self.container.setStyleSheet("QPushButton:pressed { background-color: white; }"
									"QPushButton:hover { background-color: #d9d9d9; }" 
									"QPushButton {width: 26px;"
												"height: 26px;"
												"border-radius: 13px;"
												"background-color: %s; }" % backgroundColor)
		
		# 서클 버튼 이미지
		self.button = QPushButton(self.container)
		self.button.setFocusPolicy(Qt.NoFocus)
		self.button.clicked.connect(self.clicked)
		self.button.setStyleSheet("QPushButton:pressed { background-color: #898989; }"
								"QPushButton:hover { background-color: #696969; }" 
								"QPushButton { margin-left: 4px;"
												"margin-top: 4px;"
												"width: 18px;"
												"height: 18px;"
												"border-radius: 9px;"
												"background-color: #898989;"
												"background-image: url(%s); }" % (fileName))	
												
	def enterEvent(self, event):				
		QCoreApplication.postEvent(self.button, QEvent(QEvent.Enter))

		# 손 모양 커서로 설정
		QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor))

	def leaveEvent(self, event):		
		QCoreApplication.postEvent(self.button, QEvent(QEvent.Leave))
		
		# 커서 복원
		QApplication.restoreOverrideCursor()

# 제목 표시줄(타이틀 바) 클래스
# 애플리케이션의 상단에 표시되는 제목 표시줄입니다.
# 제목 표시, 드래그 기능, 버튼을 포함합니다. 
#
class Header(QFrame):
	aboutClicked = Signal()
	closeClicked = Signal()
	
	def __init__(self, parent = None):
		super(Header, self).__init__(parent)				
		
		# 제목 설정
		self.label = QLabel(self)
		self.label.setText("<center><h3><b>Monkey Hotkey</b></h3></center>")
		self.label.setStyleSheet("color: white;" 
								"background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #969696, stop: 0.08 #5f5f5f, stop: 0.49999 #515151, stop: 0.5 #323232, stop: 0.9 #1c1c1c, stop: 1 #000000)");
		self.label.resize(_FRAME_WIDTH, _HEADER_HEIGHT)
		self.label.installEventFilter(self)
		
		# 왼쪽 버튼 - 정보 출력
		self.aboutBtn = CircleButton("html2/images/monkeyhotkey18x18.png", "#ADC6A6", self)
		self.aboutBtn.move(10, 8)		
		self.aboutBtn.clicked.connect(self.aboutClicked)

		# 오른쪽 버튼 - 종료
		self.closeBtn = CircleButton("html2/images/close-18-white.png", "white", self)
		self.closeBtn.move(_FRAME_WIDTH - 26 - 10, 8)
		self.closeBtn.clicked.connect(self.closeClicked)		

	def resizeEvent(self, event):
		# 오른쪽 버튼 재배치 
		self.closeBtn.move(event.size().width() - 26 - 10, 8)		
		
		# 제목 재배치 
		self.label.resize(event.size().width(), _HEADER_HEIGHT)			
		
	def eventFilter(self, obj, event):
		if obj == self.label:
			if event.type() == QEvent.MouseMove:
				if event.buttons() & Qt.LeftButton:
					self.parentWidget().move(event.globalPos() - self.dragPos)
					return True
				else:
					return False					
			elif event.type() == QEvent.MouseButtonPress:
				if event.button() is Qt.LeftButton:
					self.dragPos = event.globalPos() - self.parentWidget().frameGeometry().topLeft()	
					return True
				else:
					return False
			elif event.type() == QEvent.Enter:
				# 이동을 알리는 커서로 설정
				QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
				return False
			elif event.type() == QEvent.Leave:
				# 커서 복원
				QApplication.restoreOverrideCursor()
				return False
		return QFrame.eventFilter(self, obj, event)		

# 푸터(하단 바) 클래스
# 애플리케이션의 하단에 표시되는 영역입니다.
#
class Footer(QFrame):
	deleteClicked = Signal()
	setupClicked = Signal()
	infoClicked = Signal()
	
	def __init__(self, parent = None):
		super(Footer, self).__init__(parent)
		
		# 버튼 스타일 생성
		self.btnStyle = """QPushButton:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #A6A6A6, stop: 0.04 #6f6f6f, stop: 0.05 #616161, stop: 1 #3A3A3A); } 
						QPushButton {color: white; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #969696, stop: 0.04 #5f5f5f, stop: 0.05 #515151, stop: 1 #2A2A2A);
						border-width: 1px;
						border-color: #2C2C2C;
						border-style: hidden solid;}
						"""	
		# 버튼 생성
		self.deleteBtn = QPushButton(self.trUtf8('삭제'), self)
		self.deleteBtn.setIcon(QIcon("./html2/images/trash.png"))
		self.deleteBtn.setMinimumHeight(_FOOTER_HEIGHT)
		self.deleteBtn.setFocusPolicy(Qt.NoFocus)
		self.deleteBtn.setStyleSheet(self.btnStyle)
		self.deleteBtn.clicked.connect(self.deleteClicked)
		
		self.setupBtn = QPushButton(self.trUtf8('설정'), self)
		self.setupBtn.setIcon(QIcon("./html2/images/tool.png"))
		self.setupBtn.setMinimumHeight(_FOOTER_HEIGHT)
		self.setupBtn.setFocusPolicy(Qt.NoFocus)
		self.setupBtn.setStyleSheet(self.btnStyle)
		self.setupBtn.clicked.connect(self.setupClicked)
		
		self.infoBtn = QPushButton(self.trUtf8('정보'), self)
		self.infoBtn.setIcon(QIcon("./html2/images/info.png"))
		self.infoBtn.setMinimumHeight(_FOOTER_HEIGHT)
		self.infoBtn.setFocusPolicy(Qt.NoFocus)
		self.infoBtn.setStyleSheet(self.btnStyle)
		self.infoBtn.clicked.connect(self.infoClicked)		
		
		# 수평 레이아웃 설정
		layout = QHBoxLayout()
		layout.setSpacing(0)
		layout.setContentsMargins(0, 0, 0, 0)		
		layout.addWidget(self.deleteBtn)
		layout.addWidget(self.setupBtn)
		layout.addWidget(self.infoBtn)
		self.setLayout(layout)						

# About 다이얼로그 
# 프로그램에 대한 정보를 표시하는 다이얼로그입니다.
#
class AboutDialog(QDialog):
	def __init__(self, parent = None):
		super(AboutDialog, self).__init__(parent)		
		self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
		self.resize(520, 440)
		self.setFixedSize(520, 440)
		self.setWindowTitle('About - Monkey Hotkey')
		
		self.logoBackground = QLabel(self)
		self.logoBackground.setGeometry(10, 10, 148, 420)
#		self.logoBackground.setStyleSheet("""QLabel { border: 1px solid #8f8f91;
#														background-image: url('html2/images/bg_dotted.png'); }""")
		self.logoBackground.setStyleSheet("QLabel { border: 1px solid #8f8f91; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1 stop: 0 #d6d6d6, stop: 1 #737373); }");

		self.logo = QLabel(self)
		self.logo.setGeometry(20, 20, 128, 128)
		self.logo.setStyleSheet('border: none;');
		self.logo.setPixmap(QPixmap("html2/images/monkeylabs128x128.png"));
		
		fontFamily = qApp.font().family()
		if platform.system() == 'Windows':
			fontFamily = "Verdana"
			
		self.title = QLabel(self)
		self.title.move(168, 10)
		self.title.setFont(QFont(fontFamily, 12, QFont.Bold))
		self.title.setText('Monkey Hotkey 2.0.0 RC')
		
		self.content = QLabel(self)		
		if platform.system() == 'Windows':
			self.content.setGeometry(168, 40, 342, 210)
		else:
			self.content.setGeometry(168, 40, 342, 230)
		self.content.setFont(QFont(fontFamily))
		self.content.setTextFormat(Qt.RichText)
		self.content.setWordWrap(True)
		self.content.setOpenExternalLinks(True)
		self.content.setAlignment(Qt.AlignLeft | Qt.AlignTop);
		#self.content.setStyleSheet("QLabel { border: 1px solid #8f8f91;}")
		self.content.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse)
		comment = QTextCodec.codecForName("UTF-8").toUnicode("안녕하세요. 몽키 핫키 개발자입니다. 누군가를 위해서 프로그램을 만드는 것은 정말 흥미로운 일입니다. 소소한 프로그램이지만 유용하게 사용하신다면 더할 나위없이 기쁠 것 같습니다. 버그를 발견하시거나 개선했으면 하는 부분이 있으면 언제든지 문의해주세요 :D <br><br>");
		self.content.setText("""Monkey Hotkey is Free Software and may be distributed according to the terms of the GNU General Public License.<br><br>
							Copyright (C) 2009 - 2012 jungil han.<br>
							Maintainer jungil.han@gmail.com<br><br>
							""" + comment + QTextCodec.codecForName("UTF-8").toUnicode("도움주신 분들"))
		
		self.thanksArea = QTextEdit(self)
		if platform.system() == 'Windows':
			self.thanksArea.setGeometry(168, 245, 342, 150)
		else:
			self.thanksArea.setGeometry(168, 265, 342, 130)
		self.thanksArea.setReadOnly(True)
		person = "김지운 <spongetaco@gmail.com>\n"
		self.thanksArea.setText(QTextCodec.codecForName("UTF-8").toUnicode(person))
		
		self.closeButton = QPushButton("OK", self)
		self.closeButton.setGeometry(430, 405, 80, 25)
		self.closeButton.clicked.connect(self.accept)
		
# 메인 윈도우 클래스
# 애플리케이션의 컨테이너 역할을 하는 최상위 클래스입니다.
#
class MainWindow(QDialog):
	def __init__(self, parent = None):
		super(MainWindow, self).__init__(parent)		
		self.setObjectName('MainWindow')
		self.setWindowTitle('monkeyhotkey-2.0.0')
		self.setWindowIcon(QIcon("./html2/images/logo32x32.png"));
		
		# 윈도우 스타일 설정 - 타이틀바 제거
		self.setWindowFlags(Qt.CustomizeWindowHint)
				
		# 윈도우 사이즈 설정
		self.resize(_FRAME_WIDTH, _FRAME_HEIGHT)
		self.setMinimumSize(_FRAME_WIDTH,_FRAME_HEIGHT)

		# Splash 웹뷰 생성 
		self.splash = QWebView(self)
		self.splash.load(QUrl('file:///' + QDir.currentPath() + '/html2/splash.html'))
		#self.splash.raise_()
		self.splash.show()
		
		# 설정 정보 복원
		self.readSettings()
		
		# 윈도우 백그라운드 색상 설정
		palette = QPalette()
		gradient = QLinearGradient(0, 0, 0, _FRAME_HEIGHT)
		gradient.setColorAt(0, QColor('lightgray'))
		gradient.setColorAt(1, QColor('#444'))
		palette.setBrush(QPalette.Window, QBrush(gradient))
		self.setAutoFillBackground(True)
		self.setPalette(palette)
		
		# 시스템 트레이 아이콘 생성
		self.createActions()
		self.createTrayIcon()
		
		# 메인 웹뷰 생성
		self.webview = WebView(self)
		self.webview.load(QUrl('file:///' + QDir.currentPath() + '/html2/ui.html'))		
		self.webview.hide()
		
		# 헤더 생성
		self.header = Header(self)
		self.header.setMinimumSize(_FRAME_WIDTH - 4, _HEADER_HEIGHT)
		self.header.setMaximumHeight(_HEADER_HEIGHT)
		self.header.aboutClicked.connect(self.aboutClicked)
		self.header.closeClicked.connect(self.closeClicked)
		
		# 푸터 생성
		self.footer = Footer(self)
		self.footer.deleteClicked.connect(self.deleteClicked)
		self.footer.setupClicked.connect(self.setupClicked)
		self.footer.infoClicked.connect(self.infoClicked)
		if platform.system() == 'Windows':
			self.footer.setMinimumSize(_FRAME_WIDTH, _FOOTER_HEIGHT)
		else:
			self.footer.setMinimumSize(_FRAME_WIDTH - 4, _FOOTER_HEIGHT)
		self.footer.setMaximumHeight(_FOOTER_HEIGHT)		
		self.footer.hide()
		
		# 수직 레이아웃 설정
		layout = QVBoxLayout()
		layout.setSpacing(0)
		if platform.system() == 'Windows':
			layout.setContentsMargins(0, 0, 0, 0)
		else:
			layout.setContentsMargins(2, 2, 2, 2)
						
		layout.addWidget(self.header)
		layout.addWidget(self.webview)
		layout.addWidget(self.splash)
		layout.addWidget(self.footer)
		
		if platform.system() != 'Windows':
			# 메인 윈도우 왼쪽 상단에 사이즈를 조정할 수 있는 영역 생성
			self.sizeGrip = QSizeGrip(self)
			layout.addWidget(self.sizeGrip, 0, Qt.AlignBottom | Qt.AlignRight)
		
		self.setLayout(layout)

	def webView(self):
		return self.webview
		
	def ready(self):
		QTimer.singleShot(1500, self.onReady)
	
	def onReady(self):
		self.animation = QPropertyAnimation(self.splash, "pos");
		self.animation.setDuration(300);
		self.animation.setStartValue(QPoint(self.splash.pos()))
		self.animation.setEndValue(QPoint(self.splash.x(), self.splash.y() + self.splash.height()))
		self.animation.finished.connect(self.onAnimationFinished)
		self.animation.start()
		
		self.layout().removeWidget(self.splash)
		self.webview.lower()
		self.webview.show()
		self.footer.lower()
		self.footer.show()
		
	@Slot()
	def onAnimationFinished(self):
		self.splash.hide()
		del self.splash
	
	@Slot()
	def aboutClicked(self):		
		if hasattr(self, 'aboutDialog') is False:
			self.aboutDialog = AboutDialog(self)
		
		if self.aboutDialog.isVisible():
			self.aboutDialog.activateWindow()
		else:
			self.aboutDialog.show()
	
	@Slot()	
	def closeClicked(self):
		self.closeEvent(QEvent(QEvent.Close))
		
	@Slot()
	def deleteClicked(self):
		self.webview.sendMessage('showDeleteActivity');
		
	@Slot()
	def setupClicked(self):
		self.webview.sendMessage('showSetupActivity');

	@Slot()
	def infoClicked(self):
		self.webview.sendMessage('showInfoActivity');

	@Slot()
	def goWebpage(self):
		QDesktopServices.openUrl('http://code.google.com/p/monkeyhotkey/')
		
	@Slot()
	def trayQuitClicked(self):
		self.writeSettings()
		qApp.quit()
	
	def keyPressEvent(self, event):
		if event.key() == Qt.Key_Escape:
			self.webview.sendMessage('goHome');
			return True
		
		return False;
	
	def closeEvent(self, event):
		self.writeSettings()
		self.showMinimized()
		self.hide()	
		event.ignore()
		
	def writeSettings(self):
		settings = Settings()
		settings.beginGroup('MainWindow')
		settings.setValue('Size', self.size())
		settings.setValue('Position', self.pos())
		settings.endGroup()
	
	def readSettings(self):
		settings = Settings()
		settings.beginGroup('MainWindow')
		if settings.contains('Size'):		
			self.resize(QSize(settings.value('Size')))
		if settings.contains('Position'):		
			self.move(QPoint(settings.value('Position')))
		
		topmost = settings.value('Topmost', 'off')
		if (topmost == 'on'):
			self.enableTopmost(True)
			
		settings.endGroup()

	def enableAutorun(self, enable):
		#QSettings registry("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
		#QString strAppPath = QDir::currentPath() + "/Monkey Hotkey.exe";
		
		#if (a_bIsEnable)
		#    registry.setValue("Monkey Hotkey", QDir::toNativeSeparators( strAppPath ) +" /autorun");
		#else
		#    registry.remove("Monkey Hotkey");
		pass
	
	def writeSettingsAutorun(self, value):
		settings = Settings()
		settings.beginGroup('MainWindow')
		settings.setValue('Autorun', value)
		settings.endGroup()
	
	def enableTopmost(self, enable):
		flags = self.windowFlags()
		if (enable):
			flags |= Qt.WindowStaysOnTopHint
		else:
			flags &= ~Qt.WindowStaysOnTopHint
		
		self.setWindowFlags(flags)
		if (self.isVisible() == False):
			self.show()

	def writeSettingsTopmost(self, value):
		settings = Settings()
		settings.beginGroup('MainWindow')
		settings.setValue('Topmost', value)
		settings.endGroup()	

	def createActions(self):
		self.aboutAction = QAction(QTextCodec.codecForName("UTF-8").toUnicode("몽키 핫키는..."),  self, triggered=self.aboutClicked)
		self.goWebpageAction = QAction(QTextCodec.codecForName("UTF-8").toUnicode("프로젝트 홈페이지로 이동하기"),  self, triggered=self.goWebpage)
		self.quitAction = QAction(QTextCodec.codecForName("UTF-8").toUnicode("끝내기"),  self, triggered=self.trayQuitClicked)

	def createTrayIcon(self):
		self.trayMenu = QMenu()
		self.trayMenu.addAction(self.aboutAction)
		self.trayMenu.addAction(self.goWebpageAction)
		self.trayMenu.addSeparator()
		self.trayMenu.addAction(self.quitAction)
		
		self.trayIcon = QSystemTrayIcon(QIcon("./html2/images/monkeyhotkey18x18.png"), self)
		self.trayIcon.setToolTip(self.windowTitle())
		self.trayIcon.setContextMenu(self.trayMenu)
		self.trayIcon.activated.connect(self.trayIconActivated)
		self.trayIcon.show()
	
	@Slot()
	def trayIconActivated(self, reason):
		if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
			if self.isMinimized():
				self.showNormal()
			
			self.activateWindow()
			
if __name__ == '__main__':
	# Create the Qt application
	app = QApplication(sys.argv)
		
	# Create the MainWindow and show
	frame = MainWindow()
	frame.show()
	
	# Run the main Qt loop
	sys.exit(app.exec_())
